﻿#include "device.h"

device::device(QObject *parent) :
    QObject(parent)
{
    CoInitializeEx( NULL, COINIT_MULTITHREADED );
    init();
}

device::~device()
{
    SAFE_RELEASE(m_enumerator)
    SAFE_RELEASE(m_collection)
    SAFE_RELEASE(m_device)
    SAFE_RELEASE(m_audio_client)
    SAFE_RELEASE(m_audio_client_capture)

    CoUninitialize();

    if(m_out_audio_data)
    {
        delete []m_out_audio_data;
        m_out_audio_data = nullptr;
    }
}

bool device::open(int index)
{
    if(m_audio_client)
    {
        m_audio_client->Stop();
        SAFE_RELEASE(m_audio_client)
    }
    SAFE_RELEASE(m_audio_client_capture)
    SAFE_RELEASE(m_device)

    uint count = 0;
    HRESULT hr = m_collection->GetCount(&count);
    if(FAILED(hr) || count <= 0)
        return false;

    hr = m_collection->Item(index,&m_device);
    if(FAILED(hr))
        return false;
    hr = m_device->Activate(__uuidof(IAudioClient),
                            CLSCTX_ALL,
                            NULL,
                            (void**)&m_audio_client);
    if(FAILED(hr))
        return false;
    hr = m_audio_client->GetMixFormat(&m_waveformat);
    if(FAILED(hr))
        return false;
    hr = m_audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
                                    AUDCLNT_STREAMFLAGS_LOOPBACK,
                                    0,
                                    0,
                                    m_waveformat,
                                    0);
    if(FAILED(hr))
        return false;
    hr = m_audio_client->GetService(__uuidof(IAudioCaptureClient),
                                    (void**)&m_audio_client_capture);
    if(FAILED(hr))
        return false;

    initSwr();
    hr = m_audio_client->Start();
    if(FAILED(hr))
        return false;

    return true;
}

bool device::close()
{
    if(m_audio_client)
    {
        HRESULT hr = m_audio_client->Stop();
        if(FAILED(hr))
            return false;
        SAFE_RELEASE(m_audio_client)
    }
    SAFE_RELEASE(m_audio_client_capture)
    SAFE_RELEASE(m_device)
}

QStringList device::list() const
{
    return m_device_list;
}

void device::getBuffer(char **buffer, int &size, uint* nextPacketSize)
{
    if(!m_audio_client_capture || !m_audio_client)
        return;
    uchar* data = nullptr;
    uint numframe = 0;
    ulong flags;

    HRESULT hr = m_audio_client_capture->GetNextPacketSize(nextPacketSize);
    if(FAILED(hr) || nextPacketSize == 0)
    {
        *buffer = nullptr;
        size = 0;
        return;
    }
    hr = m_audio_client_capture->GetBuffer(&data,
                                           &numframe,
                                           &flags,0,0);
    if(/*flags & AUDCLNT_BUFFERFLAGS_SILENT*/numframe <= 0)
    {
        *buffer = nullptr;
        size = 0;
        return;
    }
    if(data != nullptr)
    {
        int count = swr_convert(m_swr,
                                &m_out_audio_data,
                                1024*1024,
                                (const uint8_t **)&data,
                                numframe);
        *buffer = (char*)m_out_audio_data;
        size = count*2*2;
    }
    m_audio_client_capture->ReleaseBuffer(numframe);
}

void device::init()
{
    HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                  NULL,
                                  CLSCTX_ALL,
                                  __uuidof(IMMDeviceEnumerator),
                                  (void**)&m_enumerator);
    hr = m_enumerator->EnumAudioEndpoints(eRender,
                                          DEVICE_STATE_ACTIVE,
                                          &m_collection);
    uint count = 0;
    hr = m_collection->GetCount(&count);

    LPWSTR id = NULL;
    IPropertyStore *store = NULL;
    for(uint i = 0;i < count;++i)
    {
        hr = m_collection->Item(i,&m_device);
        hr = m_device->GetId(&id);
        hr = m_device->OpenPropertyStore(STGM_READ,&store);

        PROPVARIANT name;
        PropVariantInit(&name);
        store->GetValue(PKEY_Device_FriendlyName,&name);
        m_device_list.append(QString::fromUtf16((const ushort*)name.pwszVal));

        CoTaskMemFree(id);
        PropVariantClear(&name);
        SAFE_RELEASE(store)
        SAFE_RELEASE(m_device)
    }
    SAFE_RELEASE(m_enumerator)
}

void device::initSwr()
{
    if(m_swr)
    {
        swr_free(&m_swr);
        m_swr = nullptr;
    }
    m_swr = swr_alloc();
    swr_alloc_set_opts(m_swr,
                       AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16,44100,
                       AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_FLT,m_waveformat->nSamplesPerSec,
                       0,0);
    swr_init(m_swr);
    if(!m_out_audio_data)
        m_out_audio_data = new uchar[1024*1024];
}
